﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Reflection;
using System.Windows.Forms;
using Spring.Context;
using Spring.Context.Support;
using Bamboo.Core.Common.Reflection;
using System.IO;
using TinyERP.Framework.Config;
using System.Xml;

using log4net;
using TinyERP.Framework.Controls;


namespace TinyERP.Framework.Plugin
{
    public class PluginManager
    {
        private static PluginManager instance;
        public string PluginPath { get; set; }
        private readonly ILog logger = LogManager.GetLogger(typeof(PluginManager));
        private PluginManager()
        {

        }
        public static PluginManager GetInstance()
        {
            if (instance == null)
            {
                instance = new PluginManager();
            }
            return instance;
        }

        public void LoadPlugins()
        {
            this.PluginPath = ApplicationConfiguration.GetInstance().PluginPath;
            LoadModuleAssemblies();
            
        }

        

        private void LoadModuleAssemblies()
        {
            IList<TinyERP.Framework.Config.ModuleElement> moduleList = ApplicationConfiguration.GetInstance().Modules;
            foreach (TinyERP.Framework.Config.ModuleElement module in moduleList)
            {
                LoadModuleDLL(module);
                RegisterModuleContextObjectInAssembly(module);
                LoadModuleConfig(module);
            }
            ServiceLocator.GetInstance().RefreshContext();

        }
       
        private void LoadModuleDLL(TinyERP.Framework.Config.ModuleElement module)
        {
            string assemblyName = module.UIAssembly;
            string bizAssemblyName = module.BizAssembly;
            if (ApplicationConfiguration.GetInstance().ApplicationMode == "Plugin")
            {
                AppDomain.CurrentDomain.AppendPrivatePath(PluginPath + "/" + assemblyName + "/");
            }
            LoadAssembly(assemblyName);
            LoadAssembly(bizAssemblyName);
        }
        private void LoadAssembly(string assembly)
        {
            try
            {
                AppDomain.CurrentDomain.Load(assembly);
            }
            catch (Exception ex)
            {
                logger.Error(string.Format("Can not load assembly {0}", assembly));
                logger.Error(ex);
            }

        }
        private void LoadModuleConfig(TinyERP.Framework.Config.ModuleElement module)
        {
            string assemblyKey = module.UIAssembly;
            Assembly assembly = GetAssemblyOfModule(assemblyKey);
            if (assembly == null)
            {
                logger.Error(string.Format("Can not load module config of assembly {0}", assemblyKey));
                return;
            }
            Stream stream = assembly.GetManifestResourceStream(assemblyKey + ".Config.module.xml");
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load(stream);
            module.ApplicationNodes = new List<ApplicationNodeElement>();
            XmlNodeList xmlAppNodeList = xmlDocument.SelectNodes("module/node");
            foreach (XmlNode xmlAppNode in xmlAppNodeList)
            {
                ApplicationNodeElement appNode = ApplicationNodeElement.ReadNodeInfoFromXml(xmlAppNode);
                if (appNode != null)
                {
                    module.ApplicationNodes.Add(appNode);
                }
            }
        }

        private void RegisterModuleContextObjectInAssembly(TinyERP.Framework.Config.ModuleElement module)
        {
            string assemblyKey = module.UIAssembly;
            Assembly assembly = GetAssemblyOfModule(assemblyKey);
            if (assembly == null)
            {
                logger.Error(string.Format("Can not find assembly, name = {0}", module.UIAssembly));
                return;
            }
            string typeNameOfPluginContext = assemblyKey + ".PluginContextImp";
            Type pluginContextType = assembly.GetType(typeNameOfPluginContext);
            if (pluginContextType == null)
            {
                logger.Error(string.Format("Can not find implementation class (type={0}) in module {1} assembly name={2}",
                    typeNameOfPluginContext,
                    module.Name,
                    assembly.FullName));
                return;
            }
            PluginContext pluginContextInstance = (PluginContext)Activator.CreateInstance(pluginContextType);
            pluginContextInstance.RegisterModuleObjects();
        }
        private string GetAssemblyNameFromUIClass(string uiClass)
        {
            if (string.IsNullOrEmpty(uiClass))
            {
                return null;
            }
            string[] partsOfUIClassName = uiClass.Split(',');
            if (partsOfUIClassName.Length < 1)
            {
                return null;
            }
            string assemblyName = partsOfUIClassName[1];
            assemblyName = assemblyName.Trim();
            return assemblyName;
        }

        private string GetTypeNameFromUIClass(string uiClass)
        {
            if (string.IsNullOrEmpty(uiClass))
            {
                return null;
            }
            string[] partsOfUIClassName = uiClass.Split(',');
            if (partsOfUIClassName.Length < 1)
            {
                return null;
            }
            string typeName = partsOfUIClassName[0];
            typeName = typeName.Trim();
            return typeName;
        }

        public Assembly GetAssemblyOfModule(string moduleName)
        {
            Assembly[] assArray = AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly assembly in assArray)
            {
                if (assembly.GetName().Name == moduleName)
                {
                    return assembly;
                }
            }
            return null;
        }

        public Form CreateForm(string uiClass)
        {
            string assemblyName = GetAssemblyNameFromUIClass(uiClass);
            string typeName = GetTypeNameFromUIClass(uiClass);
            Assembly assembly = GetAssemblyOfModule(assemblyName);
            if (assembly == null)
            {
                return null;
            }
            Type formType = assembly.GetType(typeName);
            Form formInstance = null;
            if (formType != null)
            {
                try
                {
                    formInstance = (Form)Activator.CreateInstance(formType);
                }
                catch (Exception ex)
                {
                    logger.Error(ex.StackTrace);
                }

            }
            return formInstance;
        }



        
    }
}
